package com.cy.cy2025.util

import android.util.Log
import com.cy.cy2025.box.Box.md5
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.job
import kotlinx.coroutines.withContext
import okhttp3.ResponseBody
import retrofit2.Retrofit
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Streaming
import retrofit2.http.Url
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue


/**
 * 文件下载工具类，支持断点续传
 */
object Downloader {

    private const val TAG = "Downloader"
    private const val BUFFER_SIZE = 4096 // 4KB缓冲区
    private val retrofit: Retrofit by lazy {
        Retrofit.Builder()
            //实际下载传入url会覆盖这个地址，这里只是为了初始化Retrofit对象
            .baseUrl("http://127.0.0.1:8080")
            .build()
    }


    //取消下载任务
    fun cancelDownload(url: String, dirPath: String) {
        // 取消协程任务
        activeJobs[url]?.let { job ->
            if (job.isActive) {
                job.cancel()
                Log.d(TAG, "已取消下载任务：$url")
            }
        }
        // 移除活跃任务和下载中URL记录
        activeJobs.remove(url)
        // 删除下载中文件（可选：保留则下次可续传，删除则下次重新下载）
        val fileMd5 = url.md5()
        val downloadingFile = File(dirPath, "$fileMd5.dl")
        if (downloadingFile.exists()) {
            downloadingFile.delete()
            Log.d(TAG, "已删除下载中文件：${downloadingFile.absolutePath}")
        }
    }

    //进度事件发布
    private val _progressEventFlow = MutableSharedFlow<ProgressEvent>()
    val progressEventFlow: SharedFlow<ProgressEvent> = _progressEventFlow


    //正在执行下载的url和协程
    private val activeJobs = ConcurrentHashMap<String, kotlinx.coroutines.Job>()


    /**
     * 检查下载状态
     * @return 0: 未下载 1: 下载中 2:上次下载中断 3: 已完成
     */
    fun checkDownloadStatus(url: String, dirPath: String): Int {
        if (activeJobs.containsKey(url)) return 1
        val fileMd5 = url.md5()
        val targetFile = File(dirPath, fileMd5)
        val downloadingFile = File(dirPath, "$fileMd5.dl")
        return when {
            targetFile.exists() -> 3
            downloadingFile.exists() -> 2
            else -> 0
        }
    }


    /**
     * 核心下载方法（挂起函数）
     * @param url 完整下载链接（支持任意服务器，如https://a.com/file、http://b.com/file）
     * @param dirPath 本地存储文件夹路径
     * @return 下载完成的文件
     * @throws Exception 下载失败（网络错误、文件操作失败等）
     */
    suspend fun download(
        url: String,
        dirPath: String
    ): File = withContext(Dispatchers.IO) {
        //对应唯一文件名
        val fileName = url.md5()
        //下载中状态文件
        val downloadingFile = File(dirPath, "$fileName.dl")
        //下载完成状态文件
        val targetFile = File(dirPath, fileName)

        // 已下载完成，直接返回
        if (targetFile.exists()) {
            Log.d(TAG, "文件已存在：${targetFile.absolutePath}")
            _progressEventFlow.emit(
                ProgressEvent(
                    url,
                    targetFile.length(),
                    targetFile.length(),
                    100
                )
            )
            return@withContext targetFile
        }
        if (activeJobs.containsKey(url)) throw Exception("正在下载中：$url")

        // 存储当前协程的 Job（关键：用于后续取消）
        val currentJob = coroutineContext.job
        activeJobs[url] = currentJob

        try {
            // 已下载大小（从.dl文件读取）
            val downloadedSize = if (downloadingFile.exists()) downloadingFile.length() else 0L

            // 创建存储目录
            val dir = File(dirPath)
            if (!dir.exists() && !dir.mkdirs()) {
                throw Exception("创建目录失败：$dirPath")
            }

            // 获取文件总大小
            val totalSize = getFileTotalSize(url) ?: throw Exception("无法获取文件大小：$url")

            // 异常情况处理（已下载大小 >= 总大小）
            if (downloadedSize >= totalSize) {
                downloadingFile.renameTo(targetFile)
                return@withContext targetFile
            }

            // 发起断点续传请求
            val downloadService = retrofit.create(DownloadService::class.java)
            val response = downloadService.downloadFile(
                range = "bytes=$downloadedSize-",
                url = url // 动态指定完整链接，忽略baseUrl
            ).execute()

            if (!response.isSuccessful || response.body() == null) {
                throw Exception("请求失败，状态码：${response.code()}")
            }

            // 服务器不支持续传则重新下载
            val actualDownloadedSize = if (response.code() == 206) downloadedSize else 0L

            // 写入文件
            response.body()?.byteStream()?.use { inputStream ->
                writeFile(
                    url = url,
                    inputStream = inputStream,
                    targetFile = downloadingFile,
                    startOffset = actualDownloadedSize,
                    totalSize = totalSize
                )
            } ?: throw Exception("响应体为空")

            // 下载完成，重命名文件
            if (!downloadingFile.renameTo(targetFile)) {
                throw Exception("文件重命名失败：${downloadingFile.name} -> ${targetFile.name}")
            }
        } finally {
            activeJobs.remove(url)
        }

        targetFile
    }

    /**
     * 获取文件总大小（通过HEAD请求，仅返回响应头，不下载文件体）
     */
    private suspend fun getFileTotalSize(url: String): Long? = withContext(Dispatchers.IO) {
        try {
            val downloadService = retrofit.create(DownloadService::class.java)
            val response = downloadService.getFileMetadata(url).execute()
            if (response.isSuccessful) {
                // 从响应头获取文件大小（Content-Length）
                response.headers()["Content-Length"]?.toLong().also { size ->
                    Log.d(TAG, "获取文件大小成功：$size 字节，URL：$url")
                }
            } else {
                Log.e(TAG, "获取文件大小失败，状态码：${response.code()}，URL：$url")
                null
            }
        } catch (e: Exception) {
            Log.e(TAG, "获取文件大小异常：${e.message}，URL：$url")
            null
        }
    }

    /**
     * 写入文件（核心辅助方法：追加模式，支持断点续传）
     * @param inputStream 网络输入流（Retrofit返回的字节流）
     * @param targetFile 下载中文件（.dl后缀）
     * @param startOffset 开始写入的偏移量（已下载大小）
     * @param totalSize 文件总大小
     * @param progressCallback 进度回调
     */
    private suspend fun writeFile(
        url: String,
        inputStream: InputStream,
        targetFile: File,
        startOffset: Long,
        totalSize: Long
    ) = withContext(Dispatchers.IO) {
        var outputStream: FileOutputStream? = null
        try {
            // 追加模式写入：从startOffset位置继续写入（不会覆盖已有内容）
            outputStream = FileOutputStream(targetFile, true)
            val buffer = ByteArray(BUFFER_SIZE)
            var bytesRead: Int
            var currentDownloaded = startOffset // 当前已下载总大小

            // 进度回调时间（每秒更新一次）
            var time = System.currentTimeMillis()
            // 循环读取流并写入文件
            while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                // 检查协程是否被取消（如Activity销毁、主动取消）
                if (!isActive) {
                    _progressEventFlow.emit(
                        ProgressEvent(
                            url,
                            currentDownloaded,
                            totalSize,
                            -1
                        )
                    )
                    Log.d(TAG, "下载已取消，当前已下载：$currentDownloaded 字节")
                    throw kotlinx.coroutines.CancellationException("下载已取消")
                }

                // 写入缓冲区数据
                outputStream.write(buffer, 0, bytesRead)
                currentDownloaded += bytesRead

                // 计算进度并回调（切换到主线程，方便更新UI）
                val progress = (currentDownloaded * 100 / totalSize).toInt()

                // 每秒更新一次进度（或下载完成时）
                if (System.currentTimeMillis() - time >= 1000 || currentDownloaded == totalSize) {
                    time = System.currentTimeMillis()
                    _progressEventFlow.emit(
                        ProgressEvent(
                            url,
                            currentDownloaded,
                            totalSize,
                            progress
                        )
                    )
                }
            }

            // 校验下载完整性（防止下载中断或文件损坏）
            if (currentDownloaded != totalSize) {
                throw Exception("下载不完整：已下载 $currentDownloaded 字节，总大小 $totalSize 字节")
            }
        } finally {
            // 静默关闭流，避免资源泄露（即使抛出异常也会执行）
            inputStream.closeQuietly()
            outputStream.closeQuietly()
        }
    }

    /**
     * 静默关闭输入流（避免空指针和IO异常）
     */
    private fun InputStream?.closeQuietly() {
        try {
            this?.close()
        } catch (e: Exception) {
            Log.w(TAG, "关闭输入流失败：${e.message}")
        }
    }

    /**
     * 静默关闭输出流（先刷新缓冲区，确保数据写入文件）
     */
    private fun FileOutputStream?.closeQuietly() {
        try {
            this?.flush() // 关键：刷新缓冲区，避免数据残留
            this?.close()
        } catch (e: Exception) {
            Log.w(TAG, "关闭输出流失败：${e.message}")
        }
    }

    /**
     * Retrofit服务接口（保持不变）
     */
    private interface DownloadService {
        @Streaming
        @GET
        fun downloadFile(
            @Header("Range") range: String,
            @Url url: String // 关键：@Url接收完整链接，覆盖baseUrl
        ): retrofit2.Call<ResponseBody>

        @GET
        fun getFileMetadata(@Url url: String): retrofit2.Call<Void>
    }
}

/**
 * 加载进度事件
 */
data class ProgressEvent(
    val url: String,
    val downloadedSize: Long,
    val totalSize: Long,
    val progress: Int = -1 // 百分比，-1表示已取消
)
